Avastage JavaScripti dekoraatorid: vÔimas metaprogrammeerimise funktsioon metaandmete lisamiseks ja AOP-mustrite rakendamiseks.
JavaScripti dekoraatorid: metaandmete programmeerimine ja AOP-mustrid
JavaScripti dekoraatorid on vĂ”imas ja vĂ€ljendusrikas metaprogrammeerimise funktsioon, mis vĂ”imaldab teil deklareerivalt ja taaskasutataval viisil muuta vĂ”i tĂ€iustada klasside, meetodite, omaduste ja parameetrite kĂ€itumist. Need pakuvad lĂŒhikest sĂŒntaksit metaandmete lisamiseks ja aspektorienteeritud programmeerimise (AOP) pĂ”himĂ”tete rakendamiseks, parandades koodi taaskasutatavust, loetavust ja hooldatavust. See pĂ”hjalik juhend uurib ĂŒksikasjalikult JavaScripti dekoraatoreid, hĂ”lmates nende sĂŒntaksit, kasutamist ja rakendusi erinevates stsenaariumides. Kuigi ametlikult on see veel arenev ettepanek, on dekoraatorid laialdaselt kasutusele vĂ”etud, eriti sellistes raamistikes nagu Angular ja NestJS, ning nende mĂ”ju JavaScripti arendusele on vaieldamatu.
Mis on JavaScripti dekoraatorid?
Dekoraatorid on spetsiaalne deklaratsioonitĂŒĂŒp, mida saab lisada klassi deklaratsioonile, meetodile, juurdepÀÀsule, omadusele vĂ”i parameetrile. Nad kasutavad @expression-i vormi, kus expression peab hindama funktsiooni, mida kutsutakse kĂ€itusajal koos teabega dekoreeritud deklaratsiooni kohta. PĂ”himĂ”tteliselt toimivad dekoraatorid funktsioonidena, mis mĂ€hivad vĂ”i muudavad dekoreeritud elementi, vĂ”imaldades teil lisada lisafunktsionaalsust vĂ”i metaandmeid ilma algkoodi otse muutmata.
MĂ”elge dekoraatoritele kui mĂ€rkustele vĂ”i markeritele, mida saab lisada koodielementidele. Neid markereid saab seejĂ€rel kĂ€itusajal töödelda erinevate ĂŒlesannete tĂ€itmiseks, nagu logimine, valideerimine, autoriseerimine vĂ”i sĂ”ltuvuse sisestamine. Dekoraatorid edendavad puhtamat ja modulaarsemat koodistruktuuri, eraldades muresid ja vĂ€hendades mallikoodi.
Dekoraatorite kasutamise eelised
- Parem koodi taaskasutatavus: Dekoraatorid vĂ”imaldavad teil kapseldada ĂŒhist kĂ€itumist taaskasutatavatesse komponentidesse, mida saab rakendada teie rakenduse mitmes osas. See vĂ€hendab koodi dubleerimist ja soodustab jĂ€rjepidevust.
- TÀiustatud loetavus: Eraldades lÀbivaid muresid dekoraatoritesse, saate oma pÔhilise loogika puhtamaks ja arusaadavamaks muuta. Dekoraatorid pakuvad deklaratiivset viisi lisakÀitumise vÀljendamiseks, muutes koodi isedokumenteerivamaks.
- Suurem hooldatavus: Dekoraatorid edendavad modulaarsust ja murede eraldamist, muutes teie rakenduse muutmist vÔi laiendamist lihtsamaks, mÔjutamata koodibaasi teisi osi. See vÀhendab vigade tekkimise riski ja lihtsustab hooldusprotsessi.
- Aspektorienteeritud programmeerimine (AOP): Dekoraatorid vÔimaldavad teil rakendada AOP-pÔhimÔtteid, vÔimaldades teil sisestada kÀitumist olemasolevasse koodi, muutmata selle lÀhtekoodi. See on eriti kasulik lÀbivate murede, nÀiteks logimise, turvalisuse ja tehingute haldamise kÀsitlemisel.
Dekoraatorite tĂŒĂŒbid
JavaScripti dekoraatoreid saab rakendada erinevat tĂŒĂŒpi deklaratsioonidele, millest igaĂŒhel on oma konkreetne eesmĂ€rk ja sĂŒntaks:
Klassi dekoraatorid
Klassi dekoraatoreid rakendatakse klassi konstruktorile ja neid saab kasutada klassi definitsiooni muutmiseks vÔi metaandmete lisamiseks. Klassi dekoraator saab argumendina klassi konstruktori.
NĂ€ide: Metaandmete lisamine klassile.
function Component(options: { selector: string, template: string }) {
return function (constructor: T) {
return class extends constructor {
selector = options.selector;
template = options.template;
}
}
}
@Component({ selector: 'my-component', template: 'Hello' })
class MyComponent {
constructor() {
// ...
}
}
console.log(new MyComponent().selector); // Output: my-component
Selles nÀites lisab Component dekoraator klassile MyComponent omadused selector ja template, vÔimaldades teil konfigureerida komponendi metaandmeid deklaratiivsel viisil. See sarnaneb Angulari komponentide mÀÀratlemisega.
Meetodi dekoraatorid
Meetodi dekoraatoreid rakendatakse klassis olevatele meetoditele ja neid saab kasutada meetodi kÀitumise muutmiseks vÔi metaandmete lisamiseks. Meetodi dekoraator saab kolm argumenti:
- Sihtobjekt (kas klassi prototĂŒĂŒp vĂ”i klassi konstruktor, olenevalt sellest, kas meetod on staatiline).
- Meetodi nimi.
- Meetodi omaduse kirjeldaja.
NÀide: MeetodikÔnede logimine.
function Log(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function (...args: any[]) {
console.log(`Calling ${propertyKey} with arguments: ${JSON.stringify(args)}`);
const result = originalMethod.apply(this, args);
console.log(`${propertyKey} returned: ${result}`);
return result;
}
return descriptor;
}
class Calculator {
@Log
add(a: number, b: number) {
return a + b;
}
}
const calculator = new Calculator();
calculator.add(2, 3); // Output: Calling add with arguments: [2,3]
// add returned: 5
Selles nÀites logib Log dekoraator meetodikÔne ja selle argumendid enne algse meetodi kÀivitamist ja logib tagastusvÀÀrtuse pÀrast tÀitmist. See on lihtne nÀide sellest, kuidas dekoraatoreid saab kasutada logimis- vÔi auditeerimisfunktsionaalsuse rakendamiseks, muutmata meetodi pÔhilist loogikat.
Omaduse dekoraatorid
Omaduse dekoraatoreid rakendatakse klassis olevatele omadustele ja neid saab kasutada omaduse kÀitumise muutmiseks vÔi metaandmete lisamiseks. Omaduse dekoraator saab kaks argumenti:
- Sihtobjekt (kas klassi prototĂŒĂŒp vĂ”i klassi konstruktor, olenevalt sellest, kas omadus on staatiline).
- Omaduse nimi.
NÀide: Omaduste vÀÀrtuste valideerimine.
function Validate(target: any, propertyKey: string) {
let value: any;
const getter = function () {
return value;
};
const setter = function (newVal: any) {
if (typeof newVal !== 'number' || newVal < 0) {
throw new Error(`Invalid value for ${propertyKey}. Must be a non-negative number.`);
}
value = newVal;
};
Object.defineProperty(target, propertyKey, {
get: getter,
set: setter,
enumerable: true,
configurable: true,
});
}
class Product {
@Validate
price: number;
constructor(price: number) {
this.price = price;
}
}
const product = new Product(10);
console.log(product.price); // Output: 10
try {
product.price = -5; // Throws an error
} catch (e) {
console.error(e.message);
}
Selles nÀites valideerib Validate dekoraator omaduse price, et veenduda, et see on mittenegatiivne arv. Kui mÀÀratakse sobimatu vÀÀrtus, visatakse viga. See on lihtne nÀide sellest, kuidas dekoraatoreid saab kasutada andmete valideerimise rakendamiseks.
Parameetri dekoraatorid
Parameetri dekoraatoreid rakendatakse meetodi parameetritele ja neid saab kasutada metaandmete lisamiseks vÔi parameetri kÀitumise muutmiseks. Parameetri dekoraator saab kolm argumenti:
- Sihtobjekt (kas klassi prototĂŒĂŒp vĂ”i klassi konstruktor, olenevalt sellest, kas meetod on staatiline).
- Meetodi nimi.
- Parameetri indeks meetodi parameetrite loendis.
NÀide: SÔltuvuste sisestamine.
import 'reflect-metadata';
const Injectable = (): ClassDecorator => {
return (target: any) => {
Reflect.defineMetadata('injectable', true, target);
};
};
const Inject = (token: string): ParameterDecorator => {
return (target: any, propertyKey: string | symbol, parameterIndex: number) => {
let existingParameters: string[] = Reflect.getOwnMetadata('parameters', target, propertyKey) || [];
existingParameters[parameterIndex] = token;
Reflect.defineMetadata('parameters', existingParameters, target, propertyKey);
};
};
@Injectable()
class Logger {
log(message: string) {
console.log(`Logger: ${message}`);
}
}
class Greeter {
private logger: Logger;
constructor(@Inject('Logger') logger: Logger) {
this.logger = logger;
}
greet(name: string) {
this.logger.log(`Hello, ${name}!`);
}
}
// Lihtne sÔltuvuse sisestamise konteiner
class Container {
private dependencies: Map = new Map();
register(token: string, dependency: any) {
this.dependencies.set(token, dependency);
}
resolve(target: any): T {
const parameters: string[] = Reflect.getMetadata('parameters', target) || [];
const resolvedDependencies = parameters.map(token => this.dependencies.get(token));
return new target(...resolvedDependencies);
}
}
const container = new Container();
container.register('Logger', new Logger());
const greeter = container.resolve(Greeter);
greeter.greet('World'); // Output: Logger: Hello, World!
Selles nÀites kasutatakse Inject dekoraatorit sÔltuvuste sisestamiseks klassi Greeter konstruktorisse. Dekoraator seob parameetriga mÀrgi, mida saab seejÀrel kasutada sÔltuvuse lahendamiseks sÔltuvuse sisestamise konteineri abil. See nÀide nÀitab sÔltuvuse sisestamise pÔhiteostust, kasutades dekoraatoreid ja teeki reflect-metadata.
Praktilised nÀited ja kasutusjuhtumid
JavaScripti dekoraatoreid saab kasutada mitmesugustes stsenaariumides koodi kvaliteedi parandamiseks ja arendamise lihtsustamiseks. Siin on mÔned praktilised nÀited ja kasutusjuhtumid:
Logimine ja auditeerimine
Dekoraatoreid saab kasutada meetodikÔnede, argumentide ja tagastusvÀÀrtuste automaatseks logimiseks, mis annab vÀÀrtuslikku teavet rakenduse kÀitumise ja jÔudluse kohta. See vÔib olla eriti kasulik vigade silumiseks ja probleemide tÔrkeotsinguks.
function LogMethod(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function (...args: any[]) {
const startTime = performance.now();
console.log(`[${new Date().toISOString()}] Calling method: ${propertyKey} with arguments: ${JSON.stringify(args)}`);
const result = originalMethod.apply(this, args);
const endTime = performance.now();
const executionTime = endTime - startTime;
console.log(`[${new Date().toISOString()}] Method ${propertyKey} returned: ${result}. Execution time: ${executionTime.toFixed(2)}ms`);
return result;
};
return descriptor;
}
class ExampleClass {
@LogMethod
complexOperation(a: number, b: number): number {
// Simuleerige aeganÔudvat toimingut
let sum = 0;
for (let i = 0; i < 1000000; i++) {
sum += a + b + i;
}
return sum;
}
}
const example = new ExampleClass();
example.complexOperation(5, 10);
See laiendatud nĂ€ide mÔÔdab meetodi tĂ€itmisaega ja logib seda koos praeguse ajatempliga, pakkudes ĂŒksikasjalikumat teavet jĂ”udluse analĂŒĂŒsiks.
Autoriseerimine ja autentimine
Dekoraatoreid saab kasutada turvapoliitika jÔustamiseks, kontrollides kasutaja rolle ja Ôigusi enne meetodi kÀivitamist. See vÔib takistada volitamata juurdepÀÀsu tundlikule teabele ja funktsionaalsusele.
function Authorize(role: string) {
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function (...args: any[]) {
const userRole = getCurrentUserRole(); // Funktsioon praeguse kasutaja rolli hankimiseks
if (userRole !== role) {
throw new Error(`Unauthorized: User does not have the required role (${role}) to access this method.`);
}
return originalMethod.apply(this, args);
};
return descriptor;
};
}
function getCurrentUserRole(): string {
// Reaalses rakenduses hangitaks see kasutaja roll autentimiskontekstist
return 'admin'; // NÀide: kÔvakodeeritud roll demonstreerimiseks
}
class AdminPanel {
@Authorize('admin')
deleteUser(userId: number) {
console.log(`User ${userId} deleted successfully.`);
}
@Authorize('editor')
editArticle(articleId: number) {
console.log(`Article ${articleId} edited successfully.`);
}
}
const adminPanel = new AdminPanel();
try {
adminPanel.deleteUser(123);
adminPanel.editArticle(456); // See tekitab vea, sest kasutaja roll on 'admin'
} catch (error) {
console.error(error.message);
}
Selles laiendatud nÀites kontrollib Authorize dekoraator, kas praegusel kasutajal on mÀÀratud roll, enne kui ta lubab meetodile juurdepÀÀsu. getCurrentUserRole funktsiooni (mis hangiks reaalses rakenduses tegeliku kasutaja rolli) kasutatakse kasutaja praeguse rolli mÀÀramiseks. Kui kasutajal pole nÔutavat rolli, visatakse viga, takistades meetodi kÀivitamist.
VahemÀlu
Dekoraatoreid saab kasutada kulukate toimingute tulemuste vahemÀllu salvestamiseks, parandades rakenduse jÔudlust ja vÀhendades serveri koormust. See vÔib olla eriti kasulik sageli kasutatavate andmete jaoks, mis ei muutu sageli.
function Cache(ttl: number = 60) { // ttl sekundites, vaikimisi 60 sekundit
const cache = new Map();
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = async function (...args: any[]) {
const cacheKey = `${propertyKey}-${JSON.stringify(args)}`;
const cachedData = cache.get(cacheKey);
if (cachedData && Date.now() < cachedData.expiry) {
console.log(`Retrieving from cache: ${propertyKey} with arguments: ${JSON.stringify(args)}`);
return cachedData.data;
}
console.log(`Executing and caching: ${propertyKey} with arguments: ${JSON.stringify(args)}`);
const result = await originalMethod.apply(this, args);
cache.set(cacheKey, {
data: result,
expiry: Date.now() + ttl * 1000, // Arvuta aegumisaeg
});
return result;
};
return descriptor;
};
}
class DataService {
@Cache(120) // VahemÀlu 120 sekundiks
async fetchData(id: number): Promise {
// Simuleerige andmete hankimist andmebaasist vÔi API-st
return new Promise((resolve) => {
setTimeout(() => {
resolve(`Data for ID ${id} fetched from source.`);
}, 1000); // Simuleerige 1-sekundilist viivitust
});
}
}
const dataService = new DataService();
(async () => {
console.log(await dataService.fetchData(1)); // KĂ€ivitab meetodi
console.log(await dataService.fetchData(1)); // Hankib vahemÀlust
await new Promise(resolve => setTimeout(resolve, 121000)); // Oota 121 sekundit, et vahemÀlu aeguks
console.log(await dataService.fetchData(1)); // KÀivitab meetodi uuesti pÀrast vahemÀlu aegumist
})();
See laiendatud nÀide rakendab lihtsa vahemÀlu mehhanismi, kasutades Map. Cache dekoraator salvestab dekoreeritud meetodi tulemused mÀÀratud aja (TTL). Kui meetodit kutsutakse uuesti samade argumentidega, tagastatakse vahemÀllu salvestatud tulemus, selle asemel et meetodit uuesti kÀivitada. PÀrast TTL-i aegumist kÀivitatakse meetod uuesti ja tulemus salvestatakse vahemÀllu.
Valideerimine
Dekoraatoreid saab kasutada andmete valideerimiseks enne nende töötlemist, tagades andmete terviklikkuse ja ennetades vigu. See vÔib olla eriti kasulik kasutajate sisestuse vÔi vÀlisallikatest saadud andmete valideerimiseks.
function Required() {
return function (target: any, propertyKey: string) {
if (!target.constructor.requiredFields) {
target.constructor.requiredFields = [];
}
target.constructor.requiredFields.push(propertyKey);
};
}
function ValidateClass(target: any) {
const originalConstructor = target;
function construct(constructor: any, args: any[]) {
const instance: any = new constructor(...args);
if (constructor.requiredFields) {
constructor.requiredFields.forEach((field: string) => {
if (!instance[field]) {
throw new Error(`Missing required field: ${field}`);
}
});
}
return instance;
}
const newConstructor: any = function (...args: any[]) {
return construct(originalConstructor, args);
};
newConstructor.prototype = originalConstructor.prototype;
return newConstructor;
}
@ValidateClass
class User {
@Required()
name: string;
@Required()
email: string;
constructor(name: string, email: string) {
this.name = name;
this.email = email;
}
}
try {
const validUser = new User('John Doe', 'john.doe@example.com');
console.log('Valid user created:', validUser);
const invalidUser = new User('Jane Doe', ''); // Puuduv e-post
} catch (error) {
console.error('Validation error:', error.message);
}
See nÀide kasutab kahte dekoraatorit: Required ja ValidateClass. Dekoraator Required mÀrgib omadused kohustuslikuks. Dekoraator ValidateClass peatab klassi konstruktori ja kontrollib, kas kÔigil kohustuslikel vÀljadel on vÀÀrtused. Kui mÔni kohustuslik vÀli puudub, visatakse viga.
SÔltuvuse sisestamine
Nagu on nÀidatud parameetri dekoraatori nÀites, vÔivad dekoraatorid hÔlbustada sÔltuvuse pÔhilist sisestamist, muutes sÔltuvuste haldamise ja komponentide lahtisidumine lihtsamaks. Kuigi on olemas keerukamaid sÔltuvuse sisestamise raamistikke, vÔivad dekoraatorid pakkuda kerget ja mugavat viisi lihtsate sÔltuvuse sisestamise stsenaariumide kÀsitlemiseks.
Kaalutlused ja parimad tavad
- MÔistke tÀitmiskonteksti: Olge teadlik dekoraatori funktsioonile edastatud argumentidest
target,propertyKeyjadescriptor. Need argumendid annavad vÀÀrtuslikku teavet dekoreeritud deklaratsiooni kohta ja vÔimaldavad teil selle kÀitumist vastavalt muuta. - Kasutage dekoraatoreid sÀÀstlikult: Kuigi dekoraatorid vÔivad olla vÔimsad, vÔib liigne kasutamine pÔhjustada keerulist ja raskesti mÔistetavat koodi. Kasutage dekoraatoreid mÔistlikult ja ainult siis, kui need annavad selge kasu koodi taaskasutatavuse, loetavuse vÔi hooldatavuse osas.
- JÀrgige nimekonventsioone: Kasutage oma dekoraatorite jaoks kirjeldavaid nimesid, et selgelt nÀidata nende eesmÀrki. See muudab teie koodi isedokumenteerivamaks ja hÔlpsamini mÔistetavaks.
- SÀilitage murede eraldamine: Dekoraatorid peaksid keskenduma konkreetsetele lÀbivatele muredele ja vÀltima omavahel mitteseotud funktsionaalsuse segamist. See parandab teie koodi modulaarsust ja hooldatavust.
- Testige oma dekoraatoreid pÔhjalikult: Nagu iga muu koodi puhul, tuleks dekoraatoreid pÔhjalikult testida, et veenduda nende Ôiges toimimises ja et nad ei pÔhjustaks soovimatuid kÔrvalmÔjusid.
- Olge ettevaatlik kÔrvalmÔjudega: Dekoraatorid kÀivituvad kÀitusajal. VÀltige dekoraatorifunktsioonides keerukaid vÔi kaua aega kestvaid toiminguid, kuna see vÔib mÔjutada rakenduse jÔudlust.
- TypeScript on soovitatav: Kuigi JavaScripti dekoraatoreid saab tehniliselt kasutada tavalises JavaScriptis koos Babeli transpileerimisega, kasutatakse neid kĂ”ige sagedamini TypeScriptiga. TypeScript pakub suurepĂ€rast tĂŒĂŒbikindlust ja kujundusaja kontrolli dekoraatorite jaoks.
Ălemaailmsed perspektiivid ja nĂ€ited
Koodi taaskasutatavuse, hooldatavuse ja murede eraldamise pÔhimÔtted, mida dekoraatorid hÔlbustavad, on universaalselt rakendatavad erinevates tarkvaraarenduse kontekstides kogu maailmas. Kuid konkreetsed rakendused ja kasutusjuhtumid vÔivad erineda sÔltuvalt tehnoloogiapakis, projekti nÔuetest ja arendustegevusest, mis on erinevates piirkondades levinud.
NĂ€iteks ettevĂ”tte Java arenduses kasutatakse konfiguratsiooni ja sĂ”ltuvuse sisestamiseks (nt Spring Framework) laialdaselt annotatsioone (sarnased mĂ”istelt dekoraatoritega). Kuigi sĂŒntaks ja aluseks olevad mehhanismid erinevad JavaScripti dekoraatoritest, jÀÀvad metaprogrammeerimise ja AOP pĂ”himĂ”tted samaks. Samamoodi on Pythonis dekoraatorid esmaklassiline keelefunktsioon ja neid kasutatakse sageli selliste ĂŒlesannete jaoks nagu logimine, autentimine ja vahemĂ€llu salvestamine.
Rahvusvahelistes meeskondades töötamisel vÔi globaalse publikuga avatud lÀhtekoodiga projektides osalemisel on oluline jÀrgida kodeerimisstandardeid ja parimaid tavasid, mis edendavad selgust ja hooldatavust. Dekoraatorite tÔhus kasutamine vÔib aidata kaasa modulaarsemale ja hÀsti struktureeritud koodibaasile, muutes eri taustaga arendajate koostöö ja panuse lihtsamaks.
JĂ€reldus
JavaScripti dekoraatorid on vĂ”imas ja mitmekĂŒlgne metaprogrammeerimise funktsioon, mis vĂ”ib oluliselt parandada koodi taaskasutatavust, loetavust ja hooldatavust. Deklaratiivse viisi pakkumisega metaandmete lisamiseks ja AOP-pĂ”himĂ”tete rakendamiseks vĂ”imaldavad dekoraatorid teil kapseldada ĂŒhist kĂ€itumist, eraldada muresid ja luua modulaarsemaid ja hĂ€sti struktureeritud rakendusi. Kuigi see on veel aktiivselt arendatav ettepanek, on dekoraatorid juba leidnud laialdast kasutuselevĂ”ttu sellistes raamistikes nagu Angular ja NestJS ning nad on valmis muutuma ĂŒha olulisemaks osaks JavaScripti ökosĂŒsteemist. Dekoraatorite sĂŒntaksi, kasutamise ja parimate tavade mĂ”istmisega saate kasutada nende vĂ”imu tugevamate, skaleeritavamate ja hooldatavamate rakenduste loomiseks.
Kuna JavaScripti ökosĂŒsteem areneb pidevalt, on uute funktsioonide ja parimate tavadega kursis pĂŒsimine ĂŒlioluline kvaliteetse tarkvara loomiseks, mis vastab kasutajate vajadustele kogu maailmas. JavaScripti dekoraatorite valdamine on vÀÀrtuslik oskus, mis aitab teil olla tĂ”husam ja produktiivsem arendaja.